#pragma once

#include <utility>
#include <deque>
#include <mutex>

#include "../kernel/utils.hh"
#include "impl.hh"
#include "../meta/constraints.hh"

namespace icesp
{
#ifdef __LOCK_QUEUE
    // small concurrent queue with lock
    template <
        int CAPACITY = utils::capacity,
        class T = impl::item
    >
    struct small_concurrent_queue
    {
        using value_type = T;
        using mutex_type = std::mutex;
        using lock_guard = std::lock_guard<mutex_type>;

        bool push(value_type x)
        {
            lock_guard _{m};
            if (count == CAPACITY) return false;
            queue.emplace_back(std::move(x));
            count++;
            return true;
        }

        bool empty() const
        {
            lock_guard _{m};
            return queue.empty();
        }

        value_type pop()
        {
            lock_guard _{m};
            auto x = std::move(queue.front());
            queue.pop_front();
            count--;
            return x;
        }

    private:
        std::deque<value_type> queue;
        mutable mutex_type m;
        int count;
    };
#else
    // small concurrent queue
    template <
        int CAPACITY = utils::capacity,
        class T = impl::item
    >
    struct small_concurrent_queue
    {
        using value_type = T;

        small_concurrent_queue() : head(0), tail(0)
        {
            auto error = posix_memalign((void**)&data, 256, sizeof(value_type) * CAPACITY);
        }

        bool push(value_type x)
        {
            if (((tail + 1) & mask) == head) return false;
            data[tail] = x;
            tail = (tail + 1) & mask;
            return true;
        }

        bool empty() const
        {
            return head == tail;
        }

        value_type pop()
        {
            auto tmp = data[head];
            head = (head + 1) & mask;
            return tmp;
        }

    private:
        static constexpr int mask{CAPACITY - 1};
        value_type* data;
        volatile int head;
        volatile int tail;
    };
#endif

    // twod concurrent queue
    template <
        int CAPACITY = utils::capacity,
        class T = impl::item
    >
    struct twod_concurrent_queue : meta::non_transferable
    {
        using value_type = T;
        static constexpr int queue_size = CAPACITY * CAPACITY;

        twod_concurrent_queue(int nthread) : nthread(nthread)
        {
            queues = new small_concurrent_queue<CAPACITY, value_type>[queue_size];
        }

        ~twod_concurrent_queue()
        {
            delete[] queues;
        }

        small_concurrent_queue<CAPACITY, value_type>* get(int row, int col) const
        {
            return queues + row * CAPACITY + col;
        }

    private:
        // FIXME public?
        small_concurrent_queue<CAPACITY, value_type>* queues;
        int nthread;
    };
}

